package org.hive.display.utils
{
	import flash.events.*;
	
	import org.hive.Hive;
	import org.hive.display.LoaderData;
	import org.hive.events.LoaderEvent;
	import org.hive.managers.plugin.LoadManager;
	import org.hive.sound.SoundData;
	
	public class LoaderSet extends EventDispatcher
	{
	    
	    public function LoaderSet( manager:LoadManager, incrementPercent:Number=40, ...args )
        {
            super();
            _name = mkName(); 
            _loaderSetIncrementPercent = incrementPercent;
            _loadManager = manager;
            _loadManager.addLoaderSet(this)
            if( args.length ) add(args);
        }
	    
	    
		private var LOADERS:Array = [];
		public function get loaders():Array { return LOADERS; }
		public function get length():uint { return LOADERS.length; }
		
		private var _name:String; 
		public function get name():String{ return _name; }
		
		private var _isLoading:Boolean = false;
		private var _isLoaded:Boolean = false;
		public function get isLoading():Boolean { return _isLoading; }
		public function get isLoaded():Boolean { return _isLoaded; }
		
		private var _loaderSetIncrementPercent : Number;
		public function get incrementPercent():Number { return _loaderSetIncrementPercent; }
		
		private var _overallPercentArray : Array = [];
		private var _overallPercentComplete : Number = 0;
		public function get percent():Number { return _overallPercentComplete; }
		
		private var _loadManager:LoadManager;
		public function get loadManager():LoadManager{ return _loadManager; }
		
		
		
		
		
		//METHODS
		public function getLoaderPosition( ldr:* ):int
		{
		    return LOADERS.indexOf(ldr.name);
		}
        
        public function add( ...ldrs ):*
        {
            for each( var ldr:* in ldrs ){
                if( ldr is Array && (ldr as Array).length == 1 ) 
                	add( ldr[0] );
                if( ldr is String )
                	LOADERS.push(ldr);
                else if( ldr ) 
                {
                    loadManager.add(ldr);
                    LOADERS.push(ldr.name);
                }
                
            }
            return ldr;
        }
		
		public function load():void
		{
			quedLoader = LOADERS[0];

			for( var i:Number = 0; i<LOADERS.length; i++) {
				_overallPercentArray[i] = 0;
			}
			_isLoading = true;

			loadNextInSet();
		}
		
		/**
		 *
		 * @deprecated : use load() instead.
		 * @see : LoaderSet.load(). 
		 * 
		 */		
		public function startLoad():void
		{
		    load();
		}
		
		public var quedLoader:String = "";
		
		private function loadNextInSet():void
		{
			var elgibleLoaders:Array = LOADERS.filter( filterEligibleLoaders );
			
			if( elgibleLoaders.length ){
				var quedIndex:Number 	= elgibleLoaders.indexOf( quedLoader );
				quedIndex 				= quedIndex == -1 ? 0 : quedIndex; 
				var nextIndex:Number = (quedIndex + 1 == elgibleLoaders.length ) ? 0 : quedIndex + 1;
				
				if( elgibleLoaders.length <= 1 )
				{
					doLoad( loadManager.getLoaderByName(elgibleLoaders[ 0 ]) );
					quedLoader = null;
				}
				else {
					doLoad( loadManager.getLoaderByName(elgibleLoaders[ quedIndex ]) );
					quedLoader = elgibleLoaders[ nextIndex ];
				}
			}
			else{
				checkLoaderSetComplete();
			}
		}
		
		private function filterEligibleLoaders( item:*, index:int, array:Array):Boolean
		{
			var quedIndex:Number = LOADERS.indexOf( item );
			var quedLoader:* = loadManager.getLoaderByName(LOADERS[quedIndex]);
			return (!quedLoader.isLoading && !quedLoader.isLoaded);
		}
		
		public function forceLoad( loaderName:String ):void
		{
			quedLoader = loaderName;
			loadNextInSet();
		}
		
		
		private function doLoad( ldr:* ):void
		{
			if( ldr is LoaderData ){
				if( !ldr.isLoaded && !ldr.isLoading ){
					try {
						ldr.load(null);
					} catch( e:Error ){
						if(Hive.DEBUG_MODE){trace("LoaderSet :: load() : "+e)}
					}
					
					configureListeners( ldr.contentLoaderInfo );
					ldr.isLoading = true;
				}
			}
			else if ( ldr['load'] is Function /* || ldr is URLData */ ){
				if( !ldr.isLoading && !ldr.isLoaded  ){
					ldr.load( ldr.URL );
					configureListeners( ldr );
					ldr.isLoading = true;
				}
			}
		}
		
		
		
		private function configureListeners(dispatcher:IEventDispatcher):void 
		{
            dispatcher.addEventListener(Event.OPEN, openHandler);
            dispatcher.addEventListener(ProgressEvent.PROGRESS, handleProgress);
            dispatcher.addEventListener(IOErrorEvent.IO_ERROR, handleError);
            dispatcher.addEventListener(Event.COMPLETE, handleComplete);
        }
        private function removeListeners(dispatcher:IEventDispatcher):void 
		{
            dispatcher.removeEventListener(Event.OPEN, openHandler);
            dispatcher.removeEventListener(ProgressEvent.PROGRESS, handleProgress);
            dispatcher.removeEventListener(IOErrorEvent.IO_ERROR, handleError);
            dispatcher.removeEventListener(Event.COMPLETE, handleComplete);
        }
		
		
		
		private function changeOverallPercent( percentOfLoaderData:Number, positionInLoadSet:int ):void
		{
			_overallPercentArray[positionInLoadSet] = Math.round( percentOfLoaderData );
			var num : Number = 0;
			for (var i:Number = 0; i < LOADERS.length; i++) {
				num += _overallPercentArray[i];
		    }
			
			_overallPercentComplete = num / LOADERS.length;

			dispatchEvent( new ProgressEvent( ProgressEvent.PROGRESS, false, false, _overallPercentComplete, 100 ) );
		}
		
		public function setPause( name:String ):void
		{
			_isLoading = false;
			var ldr:Object;
			for each( var element:String in LOADERS ){
			    ldr = _loadManager.getLoaderByName( element );
				if (element != name && ldr.isLoading ){
					try {
						ldr.close();
						removeListeners( IEventDispatcher(ldr) );
					} catch( e:Error ){
					}
				}
			}
		}
		
		public function setResume():void
		{
			_isLoading = true;
			LOADERS.forEach( resume );
		}
		
		private function pause( element:*, index:Number, arr:Array ):void
		{
		    var ldr:Object = _loadManager.getLoaderByName( element );
			if( ldr.isLoading ){
				try {
					removeListeners( IEventDispatcher(ldr) );
					try {
						ldr.close();
					} catch( e:Error ){
						if(Hive.DEBUG_MODE){trace(e)}
					}
				} catch( e:Error ){
					if(Hive.DEBUG_MODE){trace(e)}
				}
			}
			
		}
		
		private function resume( element:*, index:Number, arr:Array ):void
		{
		    var ldr:Object = _loadManager.getLoaderByName( element );
			if( ldr.isLoading )
			ldr.load( ldr.URL );
		}
		
		

		
		
		
		//EVENTS
		

        private function openHandler(e:Event):void {
			_loadManager.getLoaderByContent(e.currentTarget).isLoading = true;
        }
		
		private function handleProgress( e:ProgressEvent ):void
		{
			var percent : Number = Math.round( (e.bytesLoaded / e.bytesTotal) * 100 );
			var ldr:Object = _loadManager.getLoaderByContent(e.currentTarget);
			
            changeOverallPercent( Math.min(percent,100), getLoaderPosition( ldr ) );
				
			if( percent >= _loaderSetIncrementPercent ){
				loadNextInSet();
			}
		}
		
		private function checkHandlers( element:*, index:Number, arr:Array):Boolean
		{
			return (loadManager.getLoaderByName(element).isLoaded || loadManager.getLoaderByName(element).hasError);
		}
		
		private function handleComplete( e:Event ):void
		{
			if(Hive.DEBUG_MODE){trace("LoadManager :: Complete : " + _loadManager.getLoaderByContent(e.currentTarget).name)}

			var ldr:Object = _loadManager.getLoaderByContent(e.currentTarget);

			if( ldr is LoaderData )	removeListeners( ldr.contentLoaderInfo );
			else if ( ldr is SoundData/*  || ldr is URLData */ ){
				removeListeners(ldr as IEventDispatcher);
			}
			ldr.isLoading = false;
			ldr.isLoaded = true;
			
			dispatchEvent(new LoaderEvent(LoaderEvent.ADDED, ldr));
							
			checkLoaderSetComplete();
		}
		
		protected function checkLoaderSetComplete( ):Boolean
		{
			if( LOADERS.filter( checkHandlers ).length == LOADERS.length ){
				_isLoading = false;
				_isLoaded = true;
			    dispatchEvent( new Event( Event.COMPLETE ) );
				return true;
			}else{
	       		return false;
            }
		}
		
		private function handleError( e:IOErrorEvent ):void
		{
			if(Hive.DEBUG_MODE){trace("LoadManager :: handleError : " + e )}
            //TODO: add stopLoadOnError property 
   			//dispatchEvent( new IOErrorEvent( IOErrorEvent.IO_ERROR, false, false, e.text ) );
			_loadManager.getLoaderByContent(e.currentTarget).hasError = true;
			loadNextInSet();
		}
		
        
		// staiic helpers
        private static var _ldrCount:int=0;
        private static function mkName():String
        {
            return 'loaderset'+(++_ldrCount);
        }
		
	}
	
}